package lrz.shiftplanner.calendar;


import java.util.HashMap;
import java.util.HashSet;

import java.util.Set;

import lrz.shiftplanner.staff.Person;

public class Assignment {
	

	
	
	
	class SingleAssignment {
		Person person;
		Shift shift;	
	}
	
	private HashMap<Person, Set<Shift>> shiftsByPerson = new HashMap<Person, Set<Shift>>();
	
	// The persons that are already assigned to a shift
	private Set<Person> persons = new HashSet<Person>(); 
	private Month month;
	
	public Assignment(Month month) {
		this.month = month;
	}
	
	public Month getMonth() {
		return month;
	}
	
	
	public DayState checkTypeConstraints(Day day) {
		return day.getState(); 
	}
	
	public Set<Day> checkConstraints() {
		Set<Day> result = new HashSet<Day>();
		for (Person p: persons) {
			Set<Shift> shifts = getShifts(p);
			for (Shift s : shifts) {
				
				// follow Constraints
				for (int c = 0; c < DayConstraints.followConstraints.length; c++) {
					if (s.type == DayConstraints.followConstraints[c][0]) {
						// construct the violating shift
						Shift vShift = new Shift();
						vShift.day = month.getNextDay(s.day);
						if (vShift.day == null)
							continue;
						vShift.type = DayConstraints.followConstraints[c][1];
						
						if (shifts.contains(vShift)) {
							result.add(vShift.day);
							result.add(s.day);
						}
					}
				}
				
				// single Constraints
				for (int c = 0; c < DayConstraints.singleConstraints.length; c++) {
					if (s.type == DayConstraints.singleConstraints[c][0]) {
						// construct the violating shift
						Shift vShift = new Shift();
						vShift.day = s.day;
						vShift.type = DayConstraints.singleConstraints[c][1];
						
						if (shifts.contains(vShift)) {
							result.add(vShift.day);
							result.add(s.day);
						}
					}
				}
			}
		}
		return result;
	}
		
	private boolean removeShift(Person person, Shift shift) {
		Set<Shift> list = shiftsByPerson.get(person);
		if (list==null)
			return false;
		boolean result = list.remove(shift);
		if (list.size()==0)
			persons.remove(person);
		shiftsByPerson.put(person, list);
		return result;
		
	}
	
	private boolean putShift(Person person, Shift shift) {
		Set<Shift> list = shiftsByPerson.get(person);
		boolean flag = true;
		if (list == null) {
			list = new HashSet<Shift>();
			list.add(shift);
			shiftsByPerson.put(person, list);
			flag = false;
		} else  {
			flag = list.contains(shift);
			list.add(shift);
			shiftsByPerson.put(person, list);
		}
		persons.add(person);
		return flag;
	}
	
	public void assign(Person person, Day day, ShiftType type) {
		Shift shift = new Shift();
		shift.day = day;
		shift.type = type;
		
		SingleAssignment ass = new SingleAssignment();
		ass.person = person;
		ass.shift = shift;
		
		if (putShift(person, shift)) {
			return;
		}
		
		// Change day data structure
		day.changeShift(type, 1);
		// Change person structure
		person.incShift(1, type == ShiftType.B);
		
		// Check constraints
		Set<Day> days = checkConstraints();
		for (int i=0;i< month.getDayCount();i++)
			month.getDay(i).isViolatingConstraint =  days.contains(month.getDay(i));
	}
	
	public void remove(Person person, Day day, ShiftType type) {
		Shift shift = new Shift();
		shift.day = day;
		shift.type = type;
		
		// Change day and person data structure only if shift was really present
		if (removeShift(person, shift)) {
			day.changeShift(type, -1);
			person.incShift(-1, type == ShiftType.B);
		}
		
		// Check constraints
		Set<Day> days = checkConstraints();
		for (int i=0;i< month.getDayCount();i++)
			month.getDay(i).isViolatingConstraint =  days.contains(month.getDay(i));
	}
	
	public void removeAll(Person person, Day day) {
		for (ShiftType t : ShiftType.values())
			remove(person, day, t);
	}
	
	public Set<Shift> getShifts(Person person) {
		return shiftsByPerson.get(person);
	}
	
	public Set<Shift> getShifts(Person person, Day day) {
		Set<Shift> shifts = shiftsByPerson.get(person);
		Set<Shift> result = new HashSet<Shift>();
		for (Shift s : shifts)
			if (s.day.getDate().equals(day.getDate()))
				result.add(s);
		return result;
	}
	
	public boolean hasShift(Person person, Shift shift) {
		Set<Shift> shifts = getShifts(person);
		if (shifts!=null)
			return shifts.contains(shifts);
		else
			return false;
	}
	
	public String getShiftsString(Set<Shift> shifts) {
		String result = "";
		// Get the types
		Set<ShiftType> types = new HashSet<ShiftType>();
		for (Shift s: shifts)
			types.add(s.type);
		for (ShiftType t: types)
				result+=t;
		return result;
	}
}

